using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Xml;
using System.Xml.Schema;

using SlimDX.Direct3D9;
using SlimDX.DirectInput;
using SlimDX.Windows;

using miciv.Rendering;

namespace miciv
{
    public class CoreManager
    {
        protected Hashtable m_htblAssemblies;
        protected Hashtable m_htblCoManagers;
        protected Hashtable m_htblTypes;
        protected Hashtable m_htblEntityPrototypes;
        protected ValidationEventHandler m_heventXml;
        protected List<Entity> m_lstEntities;
        protected List<string> m_lstFolders;
        protected CoreContext m_coreContext;
        protected Hashtable m_htblCommandProcessors;

        public CoreContext CoreContext
        {
            get
            {
                return this.m_coreContext;
            }
        }

        public CoreManager(CoreContext _coreContext)
        {
            this.m_coreContext = _coreContext;
            this.m_htblAssemblies = new Hashtable();
            this.m_htblCoManagers = new Hashtable();
            this.m_htblTypes = new Hashtable();
            this.m_htblEntityPrototypes = new Hashtable();
            this.m_heventXml = new ValidationEventHandler(this.ValidationEventHandler);
            this.m_lstEntities = new List<Entity>();
            this.m_lstFolders = new List<string>();
            this.m_htblCommandProcessors = new Hashtable();
        }

        public void LoadManagers(string _strLocation)
        {
            string[] arrFiles = Directory.GetFiles(_strLocation, "*.dll");

            foreach (string strFile in arrFiles)
            {
                this.LoadModule(strFile);
            }

            foreach (string strFile in arrFiles)
            {
                this.LoadManager(strFile);
            }
        }

        public void LoadManagersFromConfig(string _strConfig)
        {
            _strConfig = this.GetExistingFile(_strConfig);
            XmlDocument xmlConfig = this.LoadXml(_strConfig, false);
            XmlNodeList lstModuleID = xmlConfig.DocumentElement.SelectNodes("modules/module/@id");

            foreach (XmlAttribute xmlModuleID in lstModuleID)
            {
                this.LoadModule(xmlModuleID.Value);
            }

            foreach (XmlAttribute xmlModuleID in lstModuleID)
            {
                this.LoadManager(xmlModuleID.Value);
            }
        }

        public void LoadManager(string _strLocation)
        {
            object[] args = { this.m_coreContext };
            Assembly assembly = null;
            ModuleInfo moduleInfo = null;

            if (false != this.m_htblAssemblies.ContainsKey(_strLocation))
            {
                moduleInfo = this.m_htblAssemblies[_strLocation] as ModuleInfo;
                assembly = moduleInfo.m_Assembly;
            }
            else
            {
                assembly = Assembly.LoadFrom(_strLocation);
                moduleInfo = new ModuleInfo(assembly);
                this.m_htblAssemblies.Add(_strLocation, moduleInfo);
            }

            foreach (Type type in assembly.GetTypes())
            {
                if ((type.IsClass == true) && (false == type.IsGenericType) && (false != type.IsPublic))
                {
                    if (null != type.GetInterface("IComponentManager"))
                    {
                        IComponentManager manager = Activator.CreateInstance(type, args) as IComponentManager;
                        foreach (string strComponentName in manager.AvailableComponentNames)
                        {
                            moduleInfo.AddManager(strComponentName, manager);
                            this.m_htblCoManagers.Add(strComponentName, manager);
                        }
                    }
                    else if (null != type.GetInterface("IUIManager"))
                    {
                        moduleInfo.AddManager(type.Name, type);
                        this.m_htblTypes.Add(type.Name, type);
                    }
                }
            }
        }

        public void LoadModule(string _strLocation)
        {
            object[] args = { this.m_coreContext };
            Assembly assembly = null;
            ModuleInfo moduleInfo = null;

            if (false != this.m_htblAssemblies.ContainsKey(_strLocation))
            {
                moduleInfo = this.m_htblAssemblies[_strLocation] as ModuleInfo;
                assembly = moduleInfo.m_Assembly;
            }
            else
            {
                assembly = Assembly.LoadFrom(_strLocation);
                moduleInfo = new ModuleInfo(assembly);
                this.m_htblAssemblies.Add(_strLocation, moduleInfo);
            }

            foreach (Type type in assembly.GetTypes())
            {
                if ((type.IsClass == true) && (false == type.IsGenericType) && (false != type.IsPublic))
                {
                    if (null != type.GetInterface("IModule"))
                    {
                        if (null != moduleInfo.m_moduleInterface)
                        {
                            throw new Exception(string.Format("{0} has more than one public IModule. current : {1}, new : {2}", _strLocation, moduleInfo.m_moduleInterface.GetType().FullName, type.FullName));
                        }
                        IModule module = Activator.CreateInstance(type, args) as IModule;
                        if (false == module.Load())
                        {
                            throw new Exception(string.Format("{0} : {1} IModule.Load failed", _strLocation, type.FullName));
                        }
                        moduleInfo.m_moduleInterface = module;
                    }
                }
            }
        }

        public void UnloadManagers()
        {
            foreach (DictionaryEntry de in this.m_htblCoManagers)
            {
                IComponentManager protoManager = de.Value as IComponentManager;
                protoManager.Release();
            }
            foreach (DictionaryEntry de in this.m_htblAssemblies)
            {
                ModuleInfo moduleInfo = de.Value as ModuleInfo;
                if (null != moduleInfo.m_moduleInterface)
                {
                    moduleInfo.m_moduleInterface.Unload();
                }
            }
            this.m_htblEntityPrototypes.Clear();
            this.m_htblCoManagers.Clear();
            this.m_htblTypes.Clear();
            this.m_htblAssemblies.Clear();
        }

        internal IComponentPrototype CreateComponentPrototype(EntityPrototype _enProto, System.Xml.XmlElement _xmlComponent)
        {
            if (false == this.m_htblCoManagers.ContainsKey(_xmlComponent.Name))
            {
                throw new SystemException("could not find " + _xmlComponent.Name + " manager");
            }
            IComponentManager manager = this.m_htblCoManagers[_xmlComponent.Name] as IComponentManager;
            IComponentPrototype coProto = manager.CreatePrototype(_enProto, _xmlComponent);
            if (null == coProto)
            {
                throw new SystemException("could not create " + _xmlComponent.Name + " prototype");
            }
            return coProto;
        }

        public void LoadEntityPrototypes(string _strLocation)
        {
            CultureInfo currCI = CultureInfo.CurrentCulture;
            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;

            string[] arrFiles = Directory.GetFiles(_strLocation, "*.xml");
            foreach (string strFile in arrFiles)
            {
                CreateEntityPrototypeFromFile(strFile);
            }

            Thread.CurrentThread.CurrentCulture = currCI;
        }

        public XmlDocument LoadXml(string strFile, bool _bValidate)
        {
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.Load(strFile);

            if (false != _bValidate)
            {
                XmlElement xmlElement = xmlDoc.DocumentElement;
                XmlAttribute xmlSchemaFile = xmlElement.Attributes["xsi:noNamespaceSchemaLocation"];
                XmlSchemaSet xmlSchemaSet = new XmlSchemaSet();
                xmlSchemaSet.Add(null, xmlSchemaFile.Value);
                xmlDoc.Schemas = xmlSchemaSet;
                xmlDoc.Validate(this.m_heventXml);
            }

            return xmlDoc;
        }

        private void CreateEntityPrototypeFromFile(string strFile)
        {
            XmlDocument xmlDoc = LoadXml(strFile, true);
            XmlElement xmlElement = xmlDoc.DocumentElement.SelectSingleNode("entity") as XmlElement;
            XmlAttribute xmlAttr = xmlElement.Attributes["name"];
            if (false != this.m_htblEntityPrototypes.ContainsKey(xmlAttr.Value))
            {
                throw new SystemException("already existing " + xmlAttr.Value + " entity (" + strFile + " file)");
            }
            EntityPrototype enProto = new EntityPrototype(xmlElement, this.CoreContext);
            this.m_htblEntityPrototypes.Add(xmlAttr.Value, enProto);
        }

        private void ValidationEventHandler(Object sender, ValidationEventArgs e)
        {
            e.ToString();
        }

        public Entity CreateEntity(EntityPrototype _enProto)
        {
            return CreateEntity<Entity>(_enProto);
        }

        public T CreateEntity<T>(EntityPrototype _enProto) where T : Entity, new()
        {
            T entity = new T();
            entity.Initialize(_enProto);
            this.m_lstEntities.Add(entity);
            return entity;
        }

        public void ReleaseEntity(Entity _entity)
        {
            if (false != this.m_lstEntities.Contains(_entity))
            {
                _entity.Release();
                this.m_lstEntities.Remove(_entity);
            }
        }

        public IUIManager CreateUIManager(string _strManagerName)
        {
            if (false != this.m_htblTypes.ContainsKey(_strManagerName))
            {
                return Activator.CreateInstance(this.m_htblTypes[_strManagerName] as Type) as IUIManager;
            }
            return null;
        }

        public void ReleaseUIManager(IUIManager _uiManager)
        {
            _uiManager = null;
        }

        public void AddFolder(string _strFolder)
        {
            if (false == this.m_lstFolders.Contains(_strFolder))
            {
                this.m_lstFolders.Add(_strFolder);
            }
        }

        public string GetExistingFile(string _strLocation)
        {
            if (false == File.Exists(_strLocation))
            {
                foreach (string folder in this.m_lstFolders)
                {
                    string strPath = string.Format("{0}/{1}", folder, _strLocation);
                    if (false != File.Exists(strPath))
                    {
                        _strLocation = strPath;
                        break;
                    }
                }
            }
            return _strLocation;
        }

        public bool ExecuteCommand(Command _command)
        {
            if (false != this.m_htblCommandProcessors.ContainsKey(_command.Name))
            {
                CommandProcessorHandler commandProcessor = this.m_htblCommandProcessors[_command.Name] as CommandProcessorHandler;
                return commandProcessor(_command);
            }
            return false;
        }

        public bool RegisterCommandProcessor(string _commandName, CommandProcessorHandler _commandProcessor)
        {
            if (false == this.m_htblCommandProcessors.ContainsKey(_commandName))
            {
                this.m_htblCommandProcessors.Add(_commandName, _commandProcessor);
                return true;
            }
            return false;
        }

        public void UnregisterCommandProcessor(string _commandName)
        {
            if (false != this.m_htblCommandProcessors.ContainsKey(_commandName))
            {
                this.m_htblCommandProcessors.Remove(_commandName);
            }
        }

        public string GetXmlAttributeValue(XmlElement _xmlElement, string _strName, string _strDefaultValue)
        {
            return GetXmlAttributeValue(_xmlElement.Attributes, _strName, _strDefaultValue);
        }

        public string GetXmlAttributeValue(XmlAttributeCollection _xmlAttributes, string _strName, string _strDefaultValue)
        {
            XmlNode xmlNode = _xmlAttributes.GetNamedItem(_strName);
            if (null != xmlNode)
            {
                return (xmlNode as XmlAttribute).Value;
            }
            return _strDefaultValue;
        }

        public EntityPrototype GetEntityPrototype(string _strName)
        {
            if (false != this.m_htblEntityPrototypes.ContainsKey(_strName))
            {
                return this.m_htblEntityPrototypes[_strName] as EntityPrototype;
            }
            return null;
        }
    }
}
